home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ms_sh21s.zip / SH210 / SRC / SYSTEM.C < prev   
C/C++ Source or Header  |  1992-09-04  |  36KB  |  1,729 lines

  1. /* MS-DOS System Function with Swaping - system (3C)
  2.  *
  3.  * MS-DOS System - Copyright (c) 1990,1,2 Data Logic Limited.
  4.  *
  5.  * This code is subject to the following copyright restrictions:
  6.  *
  7.  * 1.  Redistribution and use in source and binary forms are permitted
  8.  *     provided that the above copyright notice is duplicated in the
  9.  *     source form.
  10.  *
  11.  * Author:
  12.  *    Ian Stewartson
  13.  *    Data Logic, Queens House, Greenhill Way
  14.  *    Harrow, Middlesex  HA1 1YR, UK.
  15.  *    istewart@datlog.co.uk or ukc!datlog!istewart
  16.  *
  17.  *    $Header: /usr/users/istewart/src/shell/sh2.1/RCS/system.c,v 2.0 1992/05/21 16:49:54 Ian_Stewartson Exp $
  18.  *
  19.  *    $Log: system.c,v $
  20.  *    Revision 2.0  1992/05/21  16:49:54  Ian_Stewartson
  21.  *    MS-Shell 2.0 Baseline release
  22.  *
  23.  *
  24.  * MODULE DEFINITION:
  25.  *
  26.  * This is a version of the standard system(3c) library function.  The only
  27.  * difference is that it supports swapping and MS-SHELL EXTENDED_LINE
  28.  * processing.
  29.  *
  30.  * To get the OS2 version, compile with -DOS2
  31.  *
  32.  * There are four macros which can be changed:
  33.  *
  34.  * GET_ENVIRON        To get a variable value from the environment
  35.  * FAIL_ENVIRON        The result on failure
  36.  * FATAL_ERROR        Handle a fatal error message
  37.  * SHELL_SWITCH        The command switch to the SHELL.
  38.  *
  39.  * This module replaces the standard Microsoft SYSTEM (3C) call.  It should
  40.  * work with most models.  It has been tested in Large and Small model.
  41.  * When you link a program using the swapper, the swapper front end
  42.  * (swap.obj) must be the first object model on the linker command line so
  43.  * that it is located immediately after the PSP.  For example:
  44.  *
  45.  *    link swap+x1+x2+x3+system,x1;
  46.  * or
  47.  *    cl -o z1 swap.obj x1.obj x2.obj x3.obj system
  48.  *
  49.  * The location of the system object is not relevent.
  50.  */
  51.  
  52. #include <sys/types.h>
  53. #include <sys/stat.h>
  54. #include <limits.h>
  55. #include <errno.h>
  56. #include <stdio.h>
  57. #include <stdlib.h>
  58. #include <ctype.h>
  59. #include <fcntl.h>
  60. #include <unistd.h>
  61. #include <string.h>
  62. #include <signal.h>
  63. #include <dirent.h>
  64. #ifdef DL_MAKE
  65. #include "make.h"
  66. #endif
  67. #ifdef OS2
  68. #define INCL_DOSSESMGR
  69. #define INCL_DOSMEMMGR
  70. #define INCL_DOSPROCESS
  71. #define INCL_WINSWITCHLIST
  72. #include <os2.h>
  73. #else
  74. #include <dos.h>
  75. #endif
  76.  
  77. /*
  78.  * Externals declared by the swapper
  79.  */
  80.  
  81. #ifndef OS2
  82. extern char far        cmd_line[];    /* Command line            */
  83. extern char far        path_line[];    /* Process path            */
  84. extern unsigned int far    SW_intr;    /* interrupt pending        */
  85. extern unsigned int far    SW_Blocks;    /* Number of blocks to read    */
  86. extern int far        SW_fp;        /* File or EMS Handler        */
  87. extern unsigned int far    SW_EMsize;    /* Number of extend memory blks    */
  88. extern unsigned long far SW_EMstart;    /* Start addr of extend mem    */
  89.  
  90. #define SWAP_TO_DISK    1        /* Swap to disk            */
  91. #define SWAP_TO_Ext    2        /* Swap to extended memory    */
  92.                     /* Not recommended - no mgt    */
  93. #define SWAP_TO_EMS    3        /* Swap to EMS            */
  94. #define SWAP_TO_XMS    4        /* Swap to XMS            */
  95.  
  96. extern unsigned int far    SW_Mode;    /* Type of swapping to do    */
  97. extern unsigned int far    SW_EMSFrame;    /* EMS Frame segment        */
  98. extern bool far        SW_I23_InShell;    /* In the shell            */
  99.  
  100. /* Functions */
  101.  
  102. extern int far        SA_spawn (char **);
  103. extern void (interrupt far *SW_I23_V) (void);    /* Int 23 address    */
  104. extern void (far    *SW_XMS_Driver) (void);    /* XMS Driver Interface    */
  105. extern int far        SW_XMS_Gversion (void);
  106. extern int far        SW_XMS_Allocate (unsigned int);
  107. extern int far        SW_XMS_Free (int);
  108. extern unsigned int far    SW_XMS_Available (void);
  109. extern void interrupt far SW_Int23 (void);    /* Int 23 New address    */
  110. extern void interrupt far SW_Int00 (void);    /* Int 00 New address    */
  111. #endif
  112.  
  113. #define FFNAME_MAX    (PATH_MAX + NAME_MAX + 3)
  114.  
  115. /* Set these to the appropriate values to get environment variables.  For
  116.  * make the following values work.  Normally, getenv and (char *)NULL should
  117.  * be used.
  118.  */
  119.  
  120. #ifdef DL_MAKE
  121. #define GET_ENVIRON(p)        GetMacroValue (p)
  122. #define FAIL_ENVIRON        Nullstr
  123. #define FATAL_ERROR(a)        PrintFatalError (a)
  124. #define SHELL_SWITCH        "-ec"
  125. #else
  126. #define FATAL_ERROR(a)        { fputs (a, stderr); fputc ('\n', stderr); exit (1); }
  127. #define GET_ENVIRON(p)        getenv (p)
  128. #define FAIL_ENVIRON        (char *)NULL
  129. #define SHELL_SWITCH        "-c"
  130. #endif
  131.  
  132. /* Declarations */
  133.  
  134.                 /* Open in create mode            */
  135. #define O_CMASK        (O_WRONLY | O_CREAT | O_TRUNC | O_TEXT)
  136.                 /* Open in create mode for swap file    */
  137. #define O_SMASK        (O_RDWR | O_CREAT | O_TRUNC | O_BINARY)
  138.  
  139. #ifndef OS2
  140. #define CMD_LINE_MAX    127    /* Max command line length        */
  141.  
  142. /* MSDOS Memory Control Block chain structure */
  143.  
  144. #pragma pack (1)
  145. struct MCB_list    {
  146.     char        MCB_type;    /* M or Z            */
  147.     unsigned int    MCB_pid;    /* Process ID            */
  148.     unsigned int    MCB_len;    /* MCB length            */
  149. };
  150. #pragma pack ()
  151.  
  152. #define MCB_CON        'M'        /* More MCB's            */
  153. #define MCB_END        'Z'        /* Last MCB's            */
  154.  
  155. /* Swap Mode */
  156.  
  157. #define SWAP_OFF    0x0000        /* No swapping            */
  158. #define SWAP_DISK    0x0001        /* Disk only            */
  159. #define SWAP_EXTEND    0x0002        /* Extended memory        */
  160. #define SWAP_EXPAND    0x0004        /* Expanded memory        */
  161.  
  162. static int    Swap_Mode = (SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND);
  163. static char    *Swap_File = (char *)NULL;
  164. static char    *NoSwapFiles = "No Swap files\n";
  165. static char    *MS_emsg = "Warning: %s Error (%x)\n";
  166. static char    *XMS_Space = "Warning: %s out of space\n";
  167. static char    *SwapFailed = "%s swap failed (%x)\n";
  168.  
  169. #else
  170. #define CMD_LINE_MAX    255        /* Max command line length    */
  171. #endif
  172.  
  173. char        DOS_CommandPath[PATH_MAX + NAME_MAX + 3];
  174. char        DOS_CommandLine[CMD_LINE_MAX];    /* Command line        */
  175.  
  176. static char    *Extend_file = (char *)NULL;
  177. static char    *Extensions [] = { "", ".exe", ".com"};
  178.  
  179. /*
  180.  * Extract field from a line
  181.  */
  182.  
  183. #define MAX_LINEFIELDS    6        /* Max number of line fields    */
  184.  
  185. typedef struct Fields {
  186.     FILE    *FP;            /* File handler            */
  187.     char    *Line;            /* Line buffer            */
  188.     int        LineLength;        /* Line Length            */
  189.     char    *Field[MAX_LINEFIELDS];    /* ptr to the start of fields    */
  190. } LineFields;
  191.  
  192. /*
  193.  * Program type
  194.  */
  195.  
  196. static struct ExecutableProcessing {
  197.     unsigned char    Flags;
  198.     unsigned char    FieldSep;
  199.     char        *Name;
  200. } ExecProcessingMode;
  201.  
  202. /* Flags set a bit to indicate program mode */
  203.  
  204. #define EP_NONE        0x00        /* Use PSP command line        */
  205. #define EP_DOSMODE    0x01        /* Use DOS mode extended line    */
  206. #define EP_UNIXMODE    0x02        /* Use UNIX mode extended line    */
  207. #define EP_NOEXPAND    0x04        /* Use -f for this command    */
  208. #define EP_ENVIRON    0x08        /* Use environ for variable    */
  209. #define EP_NOSWAP    0x10        /* Do not swap for this command    */
  210. #define EP_COMSPEC    0x20        /* Special for .bat files    */
  211. #define EP_EXPORT    0x40        /* Use -m for this command    */
  212. #define EP_CONVERT    0x80        /* Use conversion        */
  213.  
  214. /*
  215.  * Common fields in EXTENDED_LINE file
  216.  */
  217.  
  218. #define COMMON_FIELD_COUNT    4
  219.  
  220. static struct CommonFields {
  221.     char        *Name;
  222.     unsigned char    Flag;
  223. } CommonFields [] = {
  224.     { "switch",        EP_CONVERT },
  225.     { "export",        EP_EXPORT },
  226.     { "noswap",        EP_NOSWAP },
  227.     { "noexpand",    EP_NOEXPAND }
  228. };
  229.  
  230. /*
  231.  * Functions
  232.  */
  233.  
  234. #ifndef OS2
  235. static bool near    Get_XMS_Driver (void);
  236. static bool near    Get_EMS_Driver (void);
  237. static bool near    EMS_error (char *, int);
  238. static bool near    XMS_error (char *, int);
  239. static int near        XMS_Close (void);
  240. static int near        EMS_Close (void);
  241. static int near        SwapToDiskError (int, char *);
  242. static int near        SwapToMemory (int);
  243. static void near    SetUpSwapper (void);
  244. #endif
  245.  
  246. static void near    ClearExtendedLineFile (void);
  247. static int near        ExecuteProgram (char *, char **);
  248. static bool near    FindLocationOfExecutable (char *, char *);
  249. static char * near    GenerateTemporaryFileName (void);
  250. static char * near    BuildNextFullPathName (char *, char *, char *);
  251. static void near    CheckProgramMode (char *);
  252. static unsigned char near CheckForCommonOptions (LineFields *, int, int);
  253. static void near    SetCurrentDrive (unsigned int);
  254. static int near        SpawnProcess (void);
  255. static int near        BuildCommandLine (char *, char **);
  256. static char * near    GenerateFullExecutablePath (char *);
  257. static bool near    WriteToExtendedFile (int, char *);
  258. static size_t near    WhiteSpaceLength (char *, bool *);
  259. static int near        StartTheProcess (char *, char **);
  260. static char * near    ConvertPathToFormat (char *);
  261. static char * near    BuildOS2String (char **, char);
  262. static int        ExtractFieldsFromLine (LineFields *);
  263.  
  264. /*
  265.  * System function with swapping
  266.  */
  267.  
  268. int        system (char const *arg2)
  269. {
  270.     char    *argv[4];
  271.     char    *ep;
  272.     int        res, serrno, len;
  273.     char    p_name[PATH_MAX + NAME_MAX + 3];
  274.     char    cdirectory[PATH_MAX + 4];    /* Current directory    */
  275.     char    *SaveEV = (char *)NULL;
  276.  
  277. /* Set up argument array */
  278.  
  279.     argv[1] = SHELL_SWITCH;
  280.  
  281.     if ((argv[0] = GET_ENVIRON ("SHELL")) == FAIL_ENVIRON)
  282.     {
  283.     argv[0] = GET_ENVIRON ("COMSPEC");
  284.     argv[1] = "/c";
  285.     }
  286.  
  287.     if (argv[0] == FAIL_ENVIRON)
  288.     FATAL_ERROR ("No Shell available");
  289.  
  290.     argv[2] = (char *)arg2;
  291.     argv[3] = (char *)NULL;
  292.  
  293. /* Check to see if the file exists.  First check for command.com to use /
  294.  * instead of -
  295.  */
  296.  
  297.     if ((ep = strrchr (argv[0], '/')) == (char *)NULL)
  298.     ep = argv[0];
  299.  
  300.     else
  301.     ++ep;
  302.  
  303. /* Check the program mode */
  304.  
  305.     CheckProgramMode (*argv);
  306.  
  307. /* Check for command.com */
  308.  
  309. #ifndef OS2
  310.     if (!stricmp (ep, "command.com") || !stricmp (ep, "command"))
  311.     {
  312.     union REGS    r;
  313.  
  314.     r.x.ax = 0x3700;
  315.     intdos (&r, &r);
  316.  
  317.     if ((r.h.al == 0) && (_osmajor < 4))
  318.         *argv[1] = (char)(r.h.dl);
  319.  
  320.     if (ExecProcessingMode.Flags & EP_CONVERT)
  321.         ExecProcessingMode.Flags |= EP_COMSPEC;
  322.     }
  323. #endif
  324.  
  325. /* Convert arguments.  If this is COMSPEC for a batch file command, skip over
  326.  * the first switch
  327.  */
  328.  
  329.     if (ExecProcessingMode.Flags & EP_COMSPEC)
  330.     len = 2;
  331.  
  332. /*
  333.  * Convert from UNIX to DOS format: Slashes to Backslashes in paths and
  334.  * dash to slash for switches
  335.  */
  336.  
  337.     if (ExecProcessingMode.Flags & EP_CONVERT)
  338.     {
  339.     while ((ep = argv[len++]) != (char *)NULL)
  340.     {
  341.         if (*ep == '-')
  342.         *ep = '/';
  343.         
  344.         else
  345.         ConvertPathToFormat (ep);
  346.     }
  347.     }
  348.  
  349. /* Save the current directory */
  350.  
  351.     getcwd (cdirectory, PATH_MAX + 3);
  352.  
  353. /* If pass in environment, set up environment variable */
  354.  
  355.     if (ExecProcessingMode.Flags & EP_ENVIRON)
  356.     {
  357.     if ((SaveEV = GET_ENVIRON (ExecProcessingMode.Name)) != FAIL_ENVIRON)
  358.         SaveEV = strdup (ExecProcessingMode.Name);
  359.  
  360. /* Get some space for the environment variable */
  361.  
  362.     if ((ep = malloc (strlen (ExecProcessingMode.Name) + strlen (argv[1]) +
  363.               strlen (argv[2]) + 3)) == (char *)NULL)
  364.     {
  365.         if (SaveEV != (char *)NULL)
  366.         free (SaveEV);
  367.  
  368.         free (ExecProcessingMode.Name);
  369.         return -1;
  370.     }
  371.  
  372.     sprintf (ep, "%s=%s%c%s", ExecProcessingMode.Name, argv[1],
  373.          ExecProcessingMode.FieldSep, argv[2]);
  374.  
  375. /* Stick it in the environment */
  376.  
  377.     if (putenv (ep))
  378.     {
  379.         free (ExecProcessingMode.Name);
  380.         return -1;
  381.     }
  382.  
  383.     argv[1] = ExecProcessingMode.Name;
  384.     argv[2] = (char *)NULL;
  385.     }
  386.  
  387. /* Start off on the search path for the executable file */
  388.  
  389.     res = (FindLocationOfExecutable (p_name, argv[0]))
  390.         ? ExecuteProgram (p_name, argv) : -1;
  391.  
  392.     serrno = errno;
  393.  
  394. /* Restore the current directory */
  395.  
  396.     SetCurrentDrive (tolower(*cdirectory) - 'a' + 1);
  397.  
  398.     if (chdir (&cdirectory[2]) != 0)
  399.     {
  400.     fputs ("Warning: current directory reset to /\n", stderr);
  401.     chdir ("/");
  402.     }
  403.  
  404. /* Clean up environment.  Restore original value */
  405.  
  406.     if (ExecProcessingMode.Flags & EP_ENVIRON)
  407.     {
  408.     len = strlen (ExecProcessingMode.Name) + 2;
  409.  
  410.     if (SaveEV != (char *)NULL)
  411.         len += strlen (SaveEV);
  412.  
  413.     if ((ep = malloc (len)) != (char *)NULL)
  414.     {
  415.         sprintf (ep, "%s=", ExecProcessingMode.Name,
  416.              (SaveEV == (char *)NULL) ? "" : SaveEV);
  417.  
  418.         putenv (ep);
  419.     }
  420.  
  421. /* Release memory */
  422.  
  423.     if (SaveEV != (char *)NULL)
  424.         free (SaveEV);
  425.  
  426.     free (ExecProcessingMode.Name);
  427.     }
  428.  
  429.     errno = serrno;
  430.     return res;
  431. }
  432.  
  433. /* Exec or spawn the program ? */
  434.  
  435. static int near    ExecuteProgram (char *path, char **parms)
  436. {
  437.     int            res;
  438. #ifndef OS2
  439.     unsigned int    size = 0;
  440.     int            serrno;
  441.     unsigned int    c_cur = (unsigned int)(_psp - 1);
  442.     struct MCB_list far    *mp = (struct MCB_list far *)((unsigned long)c_cur << 16L);
  443. #endif
  444.  
  445. /* Check to see if the file exists */
  446.  
  447.     strcpy (DOS_CommandPath, path);
  448.  
  449. /* Check we have access to the file */
  450.  
  451.     if (access (DOS_CommandPath, F_OK) != 0)
  452.     return -1;
  453.  
  454. /* Process the command line.  If no swapping, we have executed the program */
  455.  
  456.     res = BuildCommandLine (DOS_CommandPath, parms);
  457.  
  458. #ifdef OS2
  459.     SetWindowName ();
  460.     ClearExtendedLineFile ();
  461.     return res;
  462. #else
  463.     if ((ExecProcessingMode.Flags & EP_NOSWAP) ||
  464.     (Swap_Mode == SWAP_OFF) || res)
  465.     {
  466.     ClearExtendedLineFile ();
  467.     return res;
  468.     }
  469.  
  470. /* Find the length of the swap area */
  471.  
  472.     while ((mp = (struct MCB_list far *)((unsigned long)c_cur << 16L))->MCB_type
  473.         == MCB_CON)
  474.     {
  475.     if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) &&
  476.         (mp->MCB_type != MCB_END))
  477.     {
  478.         ClearExtendedLineFile ();
  479.         FATAL_ERROR ("Fatal: Memory chain corrupt");
  480.         return -1;
  481.     }
  482.  
  483.     c_cur += (mp->MCB_len + 1);
  484.     size += mp->MCB_len + 1;
  485.     }
  486.  
  487. /*
  488.  * Convert swap size from paragraphs to 16K blocks.
  489.  */
  490.  
  491.     if (size == 0)
  492.     size = mp->MCB_len + 1;
  493.  
  494.     SW_Blocks = (size / 0x0400) + 1;
  495.  
  496. /* OK Now we've set up the FCB's, command line and opened the swap file.
  497.  * Get some sys info for the swapper and execute my little assembler
  498.  * function to swap us out
  499.  */
  500.  
  501. /* Ok - 3 methods of swapping.  First tranfer to command line to the
  502.  * swapper.
  503.  */
  504.  
  505.     SetUpSwapper ();        
  506.  
  507. /* If expanded memory - try that */
  508.  
  509.     if ((Swap_Mode & SWAP_EXPAND) && Get_EMS_Driver ())
  510.     {
  511.     SW_Mode = 3;            /* Set Expanded memory swap    */
  512.  
  513.     if ((res = SwapToMemory (SWAP_EXPAND)) != -2)
  514.         return res;
  515.     }
  516.  
  517.     if ((Swap_Mode & SWAP_EXTEND) && Get_XMS_Driver ())
  518.     {
  519.     SW_Mode = (SW_fp == -1) ? 2 : 4;/* Set Extended memory or XMS driver */
  520.  
  521.     if ((res = SwapToMemory (SWAP_EXTEND)) != -2)
  522.         return res;
  523.     }
  524.  
  525. /* Try the disk if available */
  526.  
  527.     if (Swap_Mode & SWAP_DISK)
  528.     {
  529.     if ((SW_fp = open ((Swap_File = GenerateTemporaryFileName ()),
  530.                 O_SMASK, 0600)) < 0)
  531.         return SwapToDiskError (ENOSPC, NoSwapFiles);
  532.  
  533.     SW_Mode = 1;            /* Set Disk file swap        */
  534.  
  535. /* Execute the program */
  536.  
  537.     res = SpawnProcess ();
  538.  
  539. /* Close the extended command line file */
  540.  
  541.     ClearExtendedLineFile ();
  542.  
  543. /* Close the swap file */
  544.  
  545.     serrno = errno;
  546.     close (SW_fp);
  547.     unlink (Swap_File);
  548.     errno = serrno;
  549.  
  550. /* Check for out of swap space */
  551.  
  552.     if (res == -2)
  553.         return SwapToDiskError (errno, "Swap file write failed\n");
  554.     
  555. /* Return the result */
  556.  
  557.     return res;
  558.     }
  559.  
  560. /* No swapping available - give up */
  561.  
  562.     ClearExtendedLineFile ();
  563.     fputs ("swap: All Swapping methods failed\n", stderr);
  564.     errno = ENOSPC;
  565.     return -1;
  566. #endif
  567. }
  568.  
  569. #ifndef OS2
  570. /*
  571.  * OS2 does not require swapping
  572.  *
  573.  * Get the XMS Driver information
  574.  */
  575.  
  576. static bool near Get_XMS_Driver (void)
  577. {
  578.     union REGS        or;
  579.     struct SREGS    sr;
  580.     unsigned int    SW_EMsize;    /* Number of extend memory blks    */
  581.  
  582. /* Get max Extended memory pages, and convert to 16K blocks.  If Extended
  583.  * memory swapping disabled, set to zero
  584.  */
  585.  
  586.     SW_fp = -1;                /* Set EMS/XMS handler not    */
  587.                     /* defined            */
  588.  
  589. /* Is a XMS memory driver installed */
  590.  
  591.     or.x.ax = 0x4300;
  592.     int86 (0x2f, &or, &or);
  593.  
  594.     if (or.h.al != 0x80)
  595.     {
  596.     or.x.ax = 0x8800;
  597.     int86 (0x15, &or, &or);
  598.     SW_EMsize = or.x.ax / 16;
  599.  
  600.     if ((SW_EMsize <= SW_Blocks) ||
  601.          (((long)(SW_EMstart - 0x100000L) +
  602.           ((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L))
  603.         return XMS_error (XMS_Space, 0);
  604.  
  605.     else
  606.         return TRUE;
  607.     }
  608.  
  609. /* Get the driver interface */
  610.  
  611.     or.x.ax = 0x4310;
  612.     int86x (0x2f, &or, &or, &sr);
  613.     SW_XMS_Driver = (void (far *)())((unsigned long)(sr.es) << 16L | or.x.bx);
  614.  
  615. /* Support for version 3 of XMS driver */
  616.  
  617.     if ((SW_XMS_Gversion () & 0xff00) < 0x0200)
  618.     return XMS_error ("Warning: %s Version < 2\n", 0);
  619.  
  620.     else if (SW_XMS_Available () < (SW_Blocks * 16))
  621.     return XMS_error (XMS_Space, 0);
  622.  
  623.     else if ((SW_fp = SW_XMS_Allocate (SW_Blocks * 16)) == -1)
  624.     return XMS_error (MS_emsg, errno);
  625.  
  626.     return TRUE;
  627. }
  628.  
  629. /* Get the EMS Driver information */
  630.  
  631. static bool near Get_EMS_Driver (void)
  632. {
  633.     union REGS        or;
  634.     struct SREGS    sr;
  635.     char far        *sp;
  636.  
  637. /* Set EMS/XMS handler not defined */
  638.  
  639.     SW_fp = -1;
  640.  
  641.     or.x.ax = 0x3567;
  642.     intdosx (&or, &or, &sr);
  643.  
  644.     sp = (char far *)((unsigned long)(sr.es) << 16L | 10L);
  645.  
  646. /* If not there - disable */
  647.  
  648.     if (_fmemcmp ("EMMXXXX0", sp, 8) != 0)
  649.     return EMS_error ("Warning: %s not available\n", 0);
  650.  
  651.     or.h.ah = 0x40;            /* Check status            */
  652.     int86 (0x67, &or, &or);
  653.  
  654.     if (or.h.ah != 0)
  655.     return EMS_error (MS_emsg, or.h.ah);
  656.  
  657. /* Check version greater than 3.2 */
  658.  
  659.     or.h.ah = 0x46;
  660.     int86 (0x67, &or, &or);
  661.  
  662.     if ((or.h.ah != 0) || (or.h.al < 0x32))
  663.     return EMS_error ("Warning: %s Version < 3.2\n", 0);
  664.  
  665. /*  get page frame address */
  666.  
  667.     or.h.ah = 0x41;
  668.     int86 (0x67, &or, &or);
  669.  
  670.     if (or.h.ah != 0)
  671.     return EMS_error (MS_emsg, or.h.ah);
  672.  
  673.     SW_EMSFrame = or.x.bx;        /* Save the page frame        */
  674.  
  675. /* Get the number of pages required */
  676.  
  677.     or.h.ah = 0x43;
  678.     or.x.bx = SW_Blocks;
  679.     int86 (0x67, &or, &or);
  680.  
  681.     if (or.h.ah != 0)
  682.     return EMS_error (MS_emsg, or.h.ah);
  683.  
  684. /* Save the EMS Handler */
  685.  
  686.     SW_fp = or.x.dx;
  687.  
  688. /* save EMS page map */
  689.  
  690.     or.h.ah = 0x47;
  691.     or.x.dx = SW_fp;
  692.     int86 (0x67, &or, &or);
  693.  
  694.     return (or.h.ah != 0) ? EMS_error (MS_emsg, or.h.ah) : TRUE;
  695. }
  696.  
  697. /* Print EMS error message */
  698.  
  699. static bool near EMS_error (char *s, int v)
  700. {
  701.     fprintf (stderr, s, "EMS", v);
  702.     Swap_Mode &= ~(SWAP_EXPAND);
  703.     EMS_Close ();
  704.     return FALSE;
  705. }
  706.  
  707. /* Print XMS error message */
  708.  
  709. static bool near XMS_error  (char *s, int v)
  710. {
  711.     fprintf (stderr, s, "XMS", v);
  712.     Swap_Mode &= ~(SWAP_EXTEND);
  713.     XMS_Close ();
  714.     return FALSE;
  715. }
  716.  
  717. /* If the XMS handler is defined - close it */
  718.  
  719. static int near XMS_Close (void)
  720. {
  721.     int        res = 0;
  722.  
  723. /* Release XMS page */
  724.  
  725.     if (SW_fp != -1)
  726.     res = SW_XMS_Free (SW_fp);
  727.  
  728.     SW_fp = -1;
  729.     return res;
  730. }
  731.  
  732. /* If the EMS handler is defined - close it */
  733.  
  734. static int near EMS_Close (void)
  735. {
  736.     union REGS        or;
  737.     int            res = 0;
  738.  
  739.     if (SW_fp == -1)
  740.     return 0;
  741.  
  742. /* Restore EMS page */
  743.  
  744.     or.h.ah = 0x48;
  745.     or.x.dx = SW_fp;
  746.     int86 (0x67, &or, &or);
  747.  
  748.     if (or.h.ah != 0)
  749.     res = or.h.al;
  750.  
  751.     or.h.ah = 0x45;
  752.     or.x.dx = SW_fp;
  753.     int86 (0x67, &or, &or);
  754.  
  755.     SW_fp = -1;
  756.     return (res) ? res : or.h.ah;
  757. }
  758. #endif
  759.  
  760. /*
  761.  * Find the location of an executable and return it's full path
  762.  * name
  763.  */
  764.  
  765. static bool near FindLocationOfExecutable (char *FullPath, char *name)
  766. {
  767.     register char    *sp;            /* Path pointers    */
  768.     char        *ep;
  769.     char        *xp1;
  770.     int            i;
  771.  
  772. /* Scan the path for an executable */
  773.  
  774.     sp = ((strchr (name, '/') != (char *)NULL) || (*(name + 1) == ':'))
  775.         ? "" : GET_ENVIRON ("PATH");
  776.  
  777.     do
  778.     {
  779.     sp = BuildNextFullPathName (sp, name, FullPath);
  780.     ep = &FullPath[strlen (FullPath)];
  781.  
  782. /* Get start of file name */
  783.  
  784.     if ((xp1 = strrchr (FullPath, '/')) == (char *)NULL)
  785.         xp1 = FullPath;
  786.  
  787.     else
  788.         ++xp1;
  789.  
  790. /* Look up all 3 types */
  791.  
  792.     for (i = 0; i < 3; i++)
  793.     {
  794.         strcpy (ep, Extensions[i]);
  795.  
  796.         if (access (FullPath, X_OK) == 0)
  797.         return TRUE;
  798.     }
  799.     } while (sp != (char *)NULL);
  800.  
  801. /* Not found */
  802.  
  803.     errno = ENOENT;
  804.     return FALSE;
  805. }
  806.  
  807. /*
  808.  * Generate a temporary filename
  809.  */
  810.  
  811. static char * near GenerateTemporaryFileName (void)
  812. {
  813.     static char    tmpfile[FFNAME_MAX];
  814.     char    *tmpdir;    /* Points to directory prefix of pipe    */
  815.     static int    temp_count = 0;
  816.     char    *sep = "/";
  817.  
  818. /* Find out where we should put temporary files */
  819.  
  820.     if (((tmpdir = GET_ENVIRON ("TMP")) == FAIL_ENVIRON) &&
  821.     ((tmpdir = GET_ENVIRON ("HOME")) == FAIL_ENVIRON) &&
  822.     ((tmpdir = GET_ENVIRON ("TMPDIR")) == FAIL_ENVIRON))
  823.     tmpdir = ".";
  824.  
  825.     if (strchr ("/\\", tmpdir[strlen (tmpdir) - 1]) != (char *)NULL)
  826.     sep = "";
  827.  
  828. /* Get a unique temporary file name */
  829.  
  830.     for (;;)
  831.     {
  832.     sprintf (tmpfile, "%s%ssap%.5u.tmp", tmpdir, sep, temp_count++);
  833.  
  834.     if (access (tmpfile, F_OK) != 0)
  835.         break;
  836.     }
  837.  
  838.     return tmpfile;
  839. }
  840.  
  841. /*
  842.  * Extract the next path from a string and build a new path from the
  843.  * extracted path and a file name
  844.  *
  845.  * path_s - Path string
  846.  * file_s - File name string
  847.  * output_s - Output path
  848.  */
  849.  
  850. static char * near BuildNextFullPathName (register char *path_s,
  851.                       register char *file_s, char *output_s)
  852. {
  853.     register char    *s = output_s;
  854.     int            fsize = 0;
  855.  
  856.     while (*path_s && (*path_s != ';') && (fsize++ < FFNAME_MAX))
  857.     *s++ = *path_s++;
  858.  
  859.     if ((output_s != s) && (*(s - 1) != '/') && (fsize++ < FFNAME_MAX))
  860.     *s++ = '/';
  861.  
  862.     *s = '\0';
  863.  
  864.     if (file_s != (char *)NULL)
  865.     strncpy (s, file_s, FFNAME_MAX - fsize);
  866.  
  867.     output_s[FFNAME_MAX - 1] = 0;
  868.  
  869.     return (*path_s ? ++path_s : (char *)NULL);
  870. }
  871.  
  872. #ifndef OS2
  873. /*
  874.  * Swap to Memory
  875.  */
  876.  
  877. static int near    SwapToMemory (int mode)
  878. {
  879.     int        res;
  880.     int        cr;
  881.  
  882. /* Swap and close memory handler */
  883.  
  884.     res = SpawnProcess ();
  885.  
  886.     cr = (SW_Mode != 3) ? XMS_Close () : EMS_Close ();
  887.  
  888.     if ((res != -2) && cr)        /* Report Close error ?        */
  889.     {
  890.     res = -2;
  891.     errno = cr;
  892.     }
  893.  
  894.     if (res == -2)
  895.     (SW_Mode != 3) ? XMS_error (SwapFailed, errno)
  896.                : EMS_error (SwapFailed, errno);
  897.  
  898.     else
  899.     {
  900.     ClearExtendedLineFile ();
  901.     return res;
  902.     }
  903.  
  904. /* Failed - disabled */
  905.  
  906.     Swap_Mode &= (~mode);
  907.     return res;
  908. }
  909.  
  910. /*
  911.  * Swap to disk error
  912.  */
  913.  
  914. static int near SwapToDiskError (int error, char *ErrorMessage)
  915. {
  916.  
  917. /* Close the swap file, if open */
  918.  
  919.     if (SW_fp >= 0)
  920.     close (SW_fp);
  921.  
  922. /* Clean up */
  923.  
  924.     unlink (Swap_File);
  925.     Swap_File = (char *)NULL;
  926.     Swap_Mode &= (~SWAP_DISK);
  927.     fprintf (stderr, ErrorMessage);
  928.     errno = error;
  929.     return -1;
  930. }
  931. #endif
  932.  
  933. /* Clear Extended command line file */
  934.  
  935. static void near ClearExtendedLineFile (void)
  936. {
  937.     if (Extend_file != (char *)NULL)
  938.     {
  939.     unlink (Extend_file);
  940.     free ((char *)Extend_file);
  941.     }
  942.  
  943.     Extend_file = (char *)NULL;
  944. }
  945.  
  946. /*
  947.  * Check the program type
  948.  */
  949.  
  950. static void near CheckProgramMode (char *Pname)
  951. {
  952.     char            *sp, *sp1;    /* Line pointers    */
  953.     int                nFields;
  954.     char            *SPname;
  955.     LineFields            LF;
  956.     struct ExecutableProcessing    *PMode = &ExecProcessingMode;
  957.  
  958. /* Set not found */
  959.  
  960.     PMode->Flags = EP_NONE;
  961.     PMode->Name = (char *)NULL;
  962.  
  963. /* Check not a function */
  964.  
  965.     if ((Pname == (char *)NULL) ||
  966.     ((sp = GET_ENVIRON ("EXTENDED_LINE")) == FAIL_ENVIRON))
  967.         return;
  968.  
  969. /* Get some memory for the input line and the file name */
  970.  
  971.     sp1 = ((sp1 = strrchr (Pname, '/')) == (char *)NULL)
  972.          ? Pname : sp1 + 1;
  973.  
  974.     if ((SPname = strdup (sp1)) == (char *)NULL)
  975.         return;
  976.  
  977.     if ((LF.Line = malloc (LF.LineLength = 200)) == (char *)NULL)
  978.     {
  979.     free (SPname);
  980.     return;
  981.     }
  982.  
  983. /* Remove terminating .exe etc */
  984.  
  985.     if ((sp1 = strrchr (SPname, '.')) != (char *)NULL)
  986.         *sp1 = 0;
  987.  
  988. /* Open the file */
  989.  
  990.     if ((LF.FP = fopen (sp, "rt")) == (FILE *)NULL)
  991.     {
  992.     free ((char *)LF.Line);
  993.     free (SPname);
  994.     return;
  995.     }
  996.  
  997. /* Scan for the file name */
  998.  
  999.     while ((nFields = ExtractFieldsFromLine (&LF)) != -1)
  1000.     {
  1001.         if (nFields < 2)
  1002.             continue;
  1003.  
  1004. /* Remove terminating .exe etc */
  1005.  
  1006.     if ((sp = strrchr (LF.Field[0], '.')) != (char *)NULL)
  1007.         *sp = 0;
  1008.  
  1009.         if (stricmp (LF.Field[0], SPname))
  1010.             continue;
  1011.  
  1012. /* What type? */
  1013.  
  1014.     if (stricmp (LF.Field[1], "unix") == 0)
  1015.         PMode->Flags = (unsigned char)(EP_UNIXMODE |
  1016.                 CheckForCommonOptions (&LF, nFields, 2));
  1017.  
  1018.     else if (stricmp (LF.Field[1], "dos") == 0)
  1019.         PMode->Flags = (unsigned char)(EP_DOSMODE |
  1020.                 CheckForCommonOptions (&LF, nFields, 2));
  1021.  
  1022. /* Must have a valid name and we can get memory for it */
  1023.  
  1024.     else if ((stricmp (LF.Field[1], "environ") == 0) &&
  1025.          (nFields >= 3) &&
  1026.          ((PMode->Name = strdup (LF.Field[2])) != (char *)NULL))
  1027.     {
  1028.         PMode->Flags = EP_ENVIRON;
  1029.         PMode->FieldSep = 0;
  1030.  
  1031.         if (nFields >= 4)
  1032.         PMode->FieldSep = (unsigned char)strtol (LF.Field[3],
  1033.                              (char **)NULL, 0);
  1034.  
  1035.         if (!PMode->FieldSep)
  1036.         PMode->FieldSep = ' ';
  1037.     }
  1038.  
  1039.     else
  1040.         PMode->Flags = CheckForCommonOptions (&LF, nFields, 1);
  1041.  
  1042.         break;
  1043.     }
  1044.  
  1045.     fclose (LF.FP);
  1046.     free ((char *)LF.Line);
  1047.     free (SPname);
  1048. }
  1049.  
  1050. /*
  1051.  * Check for common fields
  1052.  */
  1053.  
  1054. static unsigned char near CheckForCommonOptions (LineFields *LF, int nFields,
  1055.                          int Start)
  1056. {
  1057.     unsigned char    Flags = 0;
  1058.     int            i, j;
  1059.  
  1060.     for (i = Start; i < nFields; i++)
  1061.     {
  1062.     for (j = 0; j < COMMON_FIELD_COUNT; ++j)
  1063.     {
  1064.         if (!stricmp (LF->Field[i], CommonFields[j].Name))
  1065.         {
  1066.         Flags |= CommonFields[j].Flag;
  1067.         break;
  1068.         }
  1069.     }
  1070.     }
  1071.  
  1072.     return Flags;
  1073. }
  1074.  
  1075. /*
  1076.  * Set the current drive number and return the number of drives.
  1077.  */
  1078.  
  1079. static void near SetCurrentDrive (unsigned int drive)
  1080. {
  1081. #ifdef OS2
  1082.     DosSelectDisk ((USHORT)drive);
  1083. #else
  1084.     unsigned int    ndrives;
  1085.  
  1086.     _dos_setdrive (drive, &ndrives);
  1087. #endif
  1088. }
  1089.  
  1090. /*
  1091.  * Convert UNIX format lines to DOS format if appropriate.
  1092.  * Build Environment variable for some programs.
  1093.  */
  1094. #ifndef OS2
  1095. static int near    SpawnProcess (void)
  1096. {
  1097.     void    (interrupt far *SW_I00_V) (void);    /* Int 00 address */
  1098.     void    (interrupt far *SW_I23_V) (void);    /* Int 23 address*/
  1099.     int            res;
  1100. #if 0
  1101.     union REGS        r;
  1102.     unsigned char    Save;
  1103.  
  1104.     r.x.ax = 0x3300;
  1105.     intdos (&r, &r);
  1106.     Save = r.h.al;
  1107.     fprintf (stderr, "Break Status: %s (%u)\n", Save ? "on" : "off", Save);
  1108.  
  1109.     r.x.ax = 0x3301;
  1110.     r.h.dl = 1;
  1111.     intdos (&r, &r);
  1112.     fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
  1113. #endif
  1114.  
  1115. /*
  1116.  * Save current vectors
  1117.  */
  1118.  
  1119.     SW_I00_V = _dos_getvect (0x00);
  1120.     SW_I23_V = _dos_getvect (0x23);
  1121.  
  1122. /*
  1123.  * Set In shell flag for Interrupt 23, and set to new interrupts
  1124.  */
  1125.  
  1126.     SW_I23_InShell = 0;
  1127.     _dos_setvect (0x23, SW_Int23);
  1128.     _dos_setvect (0x00, SW_I00_V);
  1129.  
  1130.     res = SA_spawn (environ);
  1131.  
  1132. /* 
  1133.  * Restore interrupt vectors
  1134.  */
  1135.  
  1136.     _dos_setvect (0x23, SW_I23_V);
  1137.  
  1138. #if 0
  1139.     r.x.ax = 0x3300;
  1140.     intdos (&r, &r);
  1141.     fprintf (stderr, "Break Status: %s (%u)\n", r.h.al ? "on" : "off", r.h.al);
  1142.     r.x.ax = 0x3301;
  1143.     r.h.dl = Save;
  1144.     intdos (&r, &r);
  1145. #endif
  1146.  
  1147. /*
  1148.  * Check for an interrupt
  1149.  */
  1150.  
  1151.     if (SW_intr)
  1152.     raise (SIGINT);
  1153.     
  1154.     return res;
  1155. }
  1156. #endif
  1157.  
  1158. /* Set up command line.  If the EXTENDED_LINE variable is set, we create
  1159.  * a temporary file, write the argument list (one entry per line) to the
  1160.  * this file and set the command line to @<filename>.  If NOSWAPPING, we
  1161.  * execute the program because I have to modify the argument line
  1162.  */
  1163.  
  1164. static int near BuildCommandLine (char *path, char **argv)
  1165. {
  1166.     char        **pl = argv;
  1167.     int            res, fd;
  1168.     bool        found;
  1169.     char        *ep;
  1170.     char        *new_args[3];
  1171.  
  1172. /* Translate process name to MSDOS format */
  1173.  
  1174.     if (GenerateFullExecutablePath (path) == (char *)NULL)
  1175.     return -1;
  1176.  
  1177. /* Extended command line processing */
  1178.  
  1179.     Extend_file = (char *)NULL;        /* Set no file        */
  1180.     found = ((ExecProcessingMode.Flags & EP_UNIXMODE) ||
  1181.          (ExecProcessingMode.Flags & EP_DOSMODE)) ? TRUE : FALSE;
  1182.  
  1183.     if ((*(++pl) != (char *)NULL) && found)
  1184.     {
  1185.     char    **pl1 = pl;
  1186.  
  1187. /* Check parameters don't contain a re-direction parameter */
  1188.  
  1189.     while (*pl1 != (char *)NULL)
  1190.     {
  1191.         if (**(pl1++) == '@')
  1192.         {
  1193.         found = FALSE;
  1194.         break;
  1195.         }
  1196.     }
  1197.  
  1198. /* If we find it - create a temporary file and write the stuff */
  1199.  
  1200.     if ((found) &&
  1201.         ((fd = open (Extend_file = GenerateTemporaryFileName (),
  1202.              O_CMASK, 0600)) >= 0))
  1203.     {
  1204.         Extend_file = strdup (Extend_file);
  1205.  
  1206. /* Copy to end of list */
  1207.  
  1208.         while (*pl != (char *)NULL)
  1209.         {
  1210.         if (!WriteToExtendedFile (fd, *(pl++)))
  1211.             return -1;
  1212.         }
  1213.  
  1214. /* Completed write OK */
  1215.  
  1216.         close (fd);
  1217.  
  1218. /* Set up DOS_CommandLine[1] to contain the filename */
  1219.  
  1220.         memset (DOS_CommandLine, 0, CMD_LINE_MAX);
  1221.         DOS_CommandLine[1] = ' ';
  1222.         DOS_CommandLine[2] = '@';
  1223.         strcpy (&DOS_CommandLine[3], Extend_file);
  1224.         DOS_CommandLine[0] = (char)(strlen (Extend_file) + 2);
  1225.  
  1226. /* Correctly terminate DOS_CommandLine in no swap mode */
  1227.  
  1228. #ifndef OS2
  1229.         if (!(ExecProcessingMode.Flags & EP_NOSWAP) &&
  1230.         (Swap_Mode != SWAP_OFF))
  1231.         DOS_CommandLine[DOS_CommandLine[0] + 2] = 0x0d;
  1232. #endif
  1233.  
  1234. /* If the name in the file is in upper case - use \ for separators */
  1235.  
  1236.         if (ExecProcessingMode.Flags & EP_DOSMODE)
  1237.         ConvertPathToFormat (&DOS_CommandLine[2]);
  1238.  
  1239. /* OK we are ready to execute */
  1240.  
  1241. #ifndef OS2
  1242.         if ((ExecProcessingMode.Flags & EP_NOSWAP) ||
  1243.         (Swap_Mode == SWAP_OFF))
  1244.         {
  1245. #endif
  1246.         new_args[0] = *argv;
  1247.         new_args[1] = &DOS_CommandLine[1];
  1248.         new_args[2] = (char *)NULL;
  1249.  
  1250.         return StartTheProcess (path, new_args);
  1251. #ifndef OS2
  1252.         }
  1253.  
  1254.         else
  1255.         return 0;
  1256. #endif
  1257.     }
  1258.     }
  1259.  
  1260. /* Check length of Parameter list */
  1261.  
  1262.     res = 0;
  1263.     DOS_CommandLine[0] = 0;
  1264.     DOS_CommandLine[1] = 0x0d;
  1265.  
  1266. /* Skip the first parameter and get the length of the rest */
  1267.  
  1268.     if (*argv != (char *)NULL)
  1269.     {
  1270.     *(ep = DOS_CommandLine + 1) = 0;
  1271.  
  1272.     while (*pl != (char *)NULL)
  1273.     {
  1274.         res += WhiteSpaceLength (*pl, &found);
  1275.  
  1276.         if (res >= CMD_LINE_MAX)
  1277.           {
  1278.           errno = E2BIG;
  1279.           return -1;
  1280.           }
  1281.  
  1282.         if (found)
  1283.             strcat (strcat (strcat (ep, " \""), *(pl++)), "\"");
  1284.  
  1285.         else
  1286.         strcat (strcat (ep, " "), *(pl++));
  1287.       }
  1288.  
  1289.     DOS_CommandLine[res + 1] = 0x0d;
  1290.     }
  1291.  
  1292. /* Terminate the line and insert the line length */
  1293.  
  1294.     DOS_CommandLine[0] = (char)res;
  1295.  
  1296. /* If swapping disabled - just execute it */
  1297.  
  1298.     return StartTheProcess (path, argv);
  1299. }
  1300.  
  1301. /*
  1302.  * Convert the executable path to the full path name
  1303.  */
  1304.  
  1305. static char * near GenerateFullExecutablePath (char *path)
  1306. {
  1307.     char        cpath[PATH_MAX + 4];
  1308.     char        npath[PATH_MAX + NAME_MAX + 4];
  1309.     char        n1path[PATH_MAX + 4];
  1310.     char        *p;
  1311.     int            drive;
  1312.  
  1313. /* Get path in DOS format */
  1314.  
  1315.     ConvertPathToFormat (path);
  1316.  
  1317. #ifndef OS2
  1318.     strupr (path);
  1319. #else 
  1320.     if (!IsHPFSFileSystem (path))
  1321.     strupr (path);
  1322. #endif
  1323.  
  1324. /* Get the current path */
  1325.  
  1326.     getcwd (cpath, PATH_MAX + 3);
  1327.     strcpy (npath, cpath);
  1328.  
  1329. /* In current directory ? */
  1330.  
  1331.     if ((p = strrchr (path, '\\')) == (char *)NULL)
  1332.     {
  1333.      p = path;
  1334.  
  1335. /* Check for a:program case */
  1336.  
  1337.      if (*(p + 1) == ':')
  1338.      {
  1339.         p += 2;
  1340.  
  1341. /* Get the path of the other drive */
  1342.  
  1343.         _getdcwd (tolower (*path) - 'a' + 1, npath, PATH_MAX + 3);
  1344.      }
  1345.     }
  1346.  
  1347. /* In root directory */
  1348.  
  1349.     else if ((p - path) == 0)
  1350.     {
  1351.     ++p;
  1352.     strcpy (npath, "/");
  1353.     *npath = *path;
  1354.     *npath = *cpath;
  1355.     }
  1356.  
  1357.     else if (((p - path) == 2) && (*(path + 1) == ':'))
  1358.     {
  1359.     ++p;
  1360.     strcpy (npath, "/");
  1361.     *npath = *path;
  1362.     }
  1363.  
  1364. /* Find the directory */
  1365.  
  1366.     else
  1367.     {
  1368.     *(p++) = 0;
  1369.  
  1370. /* Change to the directory containing the executable */
  1371.  
  1372.     drive = (*(path + 1) == ':') ? tolower (*path) - 'a' + 1 : 0;
  1373.  
  1374. /* Save the current directory on this drive */
  1375.  
  1376.     _getdcwd (drive, n1path, PATH_MAX + 3);
  1377.  
  1378. /* Find the directory we want */
  1379.  
  1380.     if (chdir (path) < 0)
  1381.         return (char *)NULL;
  1382.  
  1383.     _getdcwd (drive, npath, PATH_MAX + 3);    /* Save its full name */
  1384.     chdir (n1path);                /* Restore the original */
  1385.  
  1386. /* Restore our original directory */
  1387.  
  1388.     if (chdir (cpath) < 0)
  1389.         return (char *)NULL;
  1390.     }
  1391.  
  1392.     if (npath[strlen (npath) - 1] != '\\')
  1393.     strcat (npath, "\\");
  1394.  
  1395.     strcat (npath, p);
  1396.     return strcpy (path, npath);
  1397. }
  1398.  
  1399. /*
  1400.  * Write to the Extended File
  1401.  */
  1402.  
  1403. static bool near WriteToExtendedFile (int fd, char *string)
  1404. {
  1405.     char    *sp = string;
  1406.     char    *cp = string;
  1407.     bool    WriteOk = TRUE;
  1408.     int        Length;
  1409.  
  1410.     if (strlen (string))
  1411.     {
  1412.  
  1413. /* Write the string, converting newlines to backslash newline */
  1414.  
  1415.     while (WriteOk && (cp != (char *)NULL))
  1416.     {
  1417.         if ((cp = strchr (sp, '\n')) != (char *)NULL)
  1418.         *cp = 0;
  1419.  
  1420.         if ((Length = strlen (sp)) && (write (fd, sp, Length) != Length))
  1421.         WriteOk = FALSE;
  1422.  
  1423.         if (WriteOk && (cp != (char *)NULL))
  1424.         WriteOk = (write (fd, "\\\n", 2) == 2) ? TRUE : FALSE;
  1425.  
  1426.         sp = cp + 1;
  1427.     }
  1428.     }
  1429.  
  1430.     if (WriteOk && (write (fd, "\n", 1) == 1))
  1431.     return TRUE;
  1432.  
  1433.     close (fd);
  1434.     ClearExtendedLineFile ();
  1435.     errno = ENOSPC;
  1436.     return FALSE;
  1437. }
  1438.  
  1439. /* Check string for white space */
  1440.  
  1441. static size_t near WhiteSpaceLength (char *s, bool *wsf)
  1442. {
  1443.     char    *os = s;
  1444.  
  1445.     *wsf = FALSE;
  1446.  
  1447.     while (*s)
  1448.     {
  1449.         if (isspace (*s))
  1450.         *wsf = TRUE;
  1451.  
  1452.     ++s;
  1453.     }
  1454.  
  1455.     return (size_t)(s - os) + (*wsf ? 3 : 1);
  1456. }
  1457.  
  1458. /*
  1459.  * Execute or spawn the process
  1460.  */
  1461.  
  1462. static int near StartTheProcess (char *path, char **argv)
  1463. {
  1464. #ifdef OS2
  1465.     void    (*sig_int)();        /* Interrupt signal        */
  1466.     int        RetVal;
  1467.     USHORT    usType;
  1468.     STARTDATA    stdata;
  1469. #endif
  1470.  
  1471. /* Is this a start session option */
  1472.  
  1473. #ifndef OS2
  1474.     return ((ExecProcessingMode.Flags & EP_NOSWAP) || (Swap_Mode == SWAP_OFF))
  1475.         ? spawnve (P_WAIT, path, argv, environ) : 0;
  1476. #else
  1477.  
  1478. /* In OS/2, we need the type of the program because PM programs have to be
  1479.  * started in a session (or at least that was the only way I could get them
  1480.  * to work).
  1481.  */
  1482.  
  1483.     if (DosQAppType (path, &usType))
  1484.     {
  1485.     errno = ENOENT;
  1486.     return -1;
  1487.     }
  1488.  
  1489. /* In OS/2, need to set signal to default so child will process it */
  1490.  
  1491.     else
  1492.     {
  1493.     sig_int = signal (SIGINT, SIG_DFL);
  1494.  
  1495.     if ((usType & 3) == WINDOWAPI)
  1496.     {
  1497.         stdata.Length = sizeof (STARTDATA);
  1498.         stdata.Related = FALSE;
  1499.         stdata.FgBg = FALSE;
  1500.         stdata.TraceOpt = 0;
  1501.         stdata.PgmTitle = (char *)NULL;
  1502.         stdata.TermQ = 0;
  1503.         stdata.Environment = (char *)NULL;    /* Build Env */
  1504.         stdata.InheritOpt = 0;
  1505.         stdata.SessionType = 3;
  1506.         stdata.IconFile = (char *)NULL;
  1507.         stdata.PgmHandle = 0L;
  1508.         stdata.PgmControl = 8;
  1509.         stdata.InitXPos = 0;
  1510.         stdata.InitYPos = 0;
  1511.         stdata.InitXSize = 100;
  1512.         stdata.InitYSize = 100;
  1513.  
  1514.         RetVal = StartTheSession (&stdata, path, argv)
  1515.  
  1516.         if (stdata.Environment != (char *)NULL)
  1517.         free (stdata.Environment);
  1518.  
  1519.         if (stdata.PgmInputs != (char *)NULL)
  1520.         free (stdata.PgmInputs);
  1521.     }
  1522.  
  1523.     else
  1524.         RetVal = spawnve (P_WAIT, path, argv, environ);
  1525.  
  1526.     signal (SIGINT, sig_int);
  1527.     }
  1528.  
  1529.     return RetVal;
  1530. #endif
  1531. }
  1532.  
  1533. /*
  1534.  * Convert path format to/from UNIX
  1535.  */
  1536.  
  1537. static char * near ConvertPathToFormat (char *path)
  1538. {
  1539.     char    *s = path;
  1540.  
  1541.     while ((path = strchr (path, '/')) != (char *)NULL)
  1542.     *path = '\\';
  1543.  
  1544.     return s;
  1545. }
  1546.  
  1547. #ifdef OS2
  1548. static int near StartTheSession (STARTDATA *SessionData, char *path,
  1549.                  char **argv)
  1550. {
  1551.     USHORT    usType;
  1552.     USHORT    idSession;
  1553.     USHORT    pid;
  1554.  
  1555. /* Ensure we always start a PM session in PM */
  1556.  
  1557.     if (DosQAppType (path, &usType))
  1558.     {
  1559.     errno = ENOENT;
  1560.     return -1;
  1561.     }
  1562.  
  1563.     if ((usType & 3) == WINDOWAPI)
  1564.     SessionData->SessionType = 3;
  1565.     
  1566.     SessionData->PgmName = path;
  1567.  
  1568.     if ((SessionData->Environment = BuildOS2String (environ, 0)) == (char *)NULL)
  1569.     return -1;
  1570.  
  1571.     if ((SessionData->PgmInputs = BuildOS2String (&argv[1], 0)) == (char *)NULL)
  1572.     return -1;
  1573.  
  1574.     if (!(usType = DosStartSession (SessionData, &idSession, &pid)))
  1575.     return 0;
  1576.     
  1577.     else
  1578.     {
  1579.     errno = ENOENT;
  1580.     return -1;
  1581.     }
  1582. }
  1583. #endif
  1584.  
  1585. /*
  1586.  * Build the OS2 format <value>\0<value>\0 etc \0
  1587.  */
  1588.  
  1589. static char * near BuildOS2String (char **Array, char sep)
  1590. {
  1591.     int        i = 0;
  1592.     int        Length = 0;
  1593.     char    *Output;
  1594.     char    *sp, *cp;
  1595.  
  1596.     while ((sp = Array[i++]) != (char *)NULL)
  1597.     Length += strlen (sp) + 1;
  1598.    
  1599.     Length += 2;
  1600.  
  1601.     if ((Output = malloc (Length)) == (char *)NULL)
  1602.     return (char *)NULL;
  1603.  
  1604.     i = 0;
  1605.     sp = Output;
  1606.  
  1607. /* Build the string */
  1608.  
  1609.     while ((cp = Array[i++]) != (char *)NULL)
  1610.     {
  1611.     while (*sp = *(cp++))
  1612.         ++sp;
  1613.     
  1614.     *(sp++) = sep;
  1615.     }
  1616.  
  1617.     *sp = 0;
  1618.     return Output;
  1619. }
  1620.  
  1621. /* 
  1622.  * Get and process configuration line:
  1623.  *
  1624.  * <field> = <field> <field> # comment
  1625.  *
  1626.  * return the number of fields found.
  1627.  */
  1628.  
  1629. static int ExtractFieldsFromLine (LineFields *fd)
  1630. {
  1631.     char    *cp;
  1632.     int        fieldno;
  1633.  
  1634.     if (fgets (fd->Line, fd->LineLength - 1, fd->FP) == (char *)NULL)
  1635.     {
  1636.     fclose (fd->FP);
  1637.     return -1;
  1638.     }
  1639.  
  1640. /* Remove the EOL */
  1641.  
  1642.     if ((cp = strchr (fd->Line, '\n')) != (char *)NULL)
  1643.     *cp = 0;
  1644.  
  1645. /* Remove the comments at end */
  1646.  
  1647.     if ((cp = strchr (fd->Line, '#')) != (char *)NULL)
  1648.     *cp = 0;
  1649.  
  1650. /* Extract the fields */
  1651.  
  1652.     cp = fd->Line;
  1653.     for (fieldno = 0; fieldno < MAX_LINEFIELDS; fieldno++)
  1654.     {
  1655.     while (isspace (*cp))
  1656.         ++cp;
  1657.  
  1658.     if (!*cp)
  1659.         return fieldno;
  1660.  
  1661.     fd->Field[fieldno] = cp;
  1662.  
  1663. /* First field must be followed by equals */
  1664.  
  1665.     if (!fieldno)
  1666.     {
  1667.         while (!isspace (*cp) && *cp && (*cp != '='))
  1668.         ++cp;
  1669.  
  1670.         if (*cp && (*cp != '='))
  1671.         {
  1672.         *(cp++) = 0;
  1673.  
  1674.         while (isspace (*cp))
  1675.             ++cp;
  1676.         }
  1677.  
  1678.         if (*cp != '=')
  1679.         return fieldno + 1;
  1680.     }
  1681.  
  1682. /* Process second and third fields */
  1683.  
  1684.     else
  1685.     {
  1686.         while (!isspace (*cp) && *cp)
  1687.         ++cp;
  1688.     }
  1689.  
  1690.     *(cp++) = 0;
  1691.     }
  1692.  
  1693.     return fieldno;
  1694. }
  1695.  
  1696. /*
  1697.  * For multiple model support, we need to transfer to command lines
  1698.  * to the swapper in far space
  1699.  */
  1700.  
  1701. #ifndef OS2
  1702. static void near SetUpSwapper (void)
  1703. {
  1704. /* Transfer path name */
  1705.  
  1706.     _fstrcpy (path_line, DOS_CommandPath);
  1707.  
  1708. /* Transfer command line */
  1709.  
  1710.     _fmemcpy (cmd_line, DOS_CommandLine, CMD_LINE_MAX);
  1711. }
  1712. #endif
  1713.  
  1714. /*
  1715.  * Test program
  1716.  */
  1717.  
  1718. #ifdef TEST
  1719. int main (int argc, char **argv)
  1720. {
  1721.     int        i;
  1722.  
  1723.     for (i = 1; i < argc; i++)
  1724.     printf ("Result = %d\n", system (argv[i]));
  1725.     
  1726.     return 0;
  1727. }
  1728. #endif
  1729.